#include "recordtask.h"
#include "getliveinfohelper.h"
#include "libs/base/basetype.h"
#include "libs/core/model/attention.h"
#include "libs/core/model/attentionmodel.h"
#include "libs/core/model/liveinfo.h"
#include "libs/core/model/livemodel.h"
#include "libs/core/model/model.h"
#include "libs/core/model/presenter.h"
#include "libs/core/model/presentermodel.h"
#include "libs/core/model/recordmodel.h"
#include "libs/core/utils/idgenerator.h"
#include "liverecorder.h"
#include "services/serverliveinfo.h"
#include "services/yingceservice.h"
#include "streamrecorderwithfailover.h"
#include "yingce.h"
#include <QJsonDocument>
#include <QJsonObject>

RecordTask::RecordTask(LiveRecorder* recorder, int attention, QObject* parent)
    : Task(0, true, 0, parent), recorder_(recorder), attention_(attention)
{
}

RecordTask::~RecordTask()
{
    if (request_id_ != 0) {
        recorder_->getLiveInfoHelper()->cancel(request_id_);
        request_id_ = 0;
    }
    if (stream_recorder_) {
        disconnect(stream_recorder_, nullptr, this, nullptr);
        delete stream_recorder_;
        stream_recorder_ = nullptr;
    }
}

void RecordTask::stop()
{
    if (request_id_ != 0) {
        recorder_->getLiveInfoHelper()->cancel(request_id_);
        request_id_ = 0;
    }
    if (stream_recorder_) {
        stream_recorder_->stop();
    } else {
        Q_EMIT recordFinished(QString(), 0);
        Q_EMIT finished();
    }
}

void RecordTask::startImpl()
{
    auto a = YingCe::instance()->model()->attentionModel()->attention(attention_);
    Q_ASSERT(a);
    auto p = YingCe::instance()->model()->presenterModel()->presenter(a->presenter);
    Q_ASSERT(p);
    if (p->status == yc::ACTIVE_LIVE_STATUS && startRecord(p->active_live)) {
        return;
    }

    const auto now = QDateTime::currentDateTime();
    if (p->update_time.msecsTo(now) > 5000) {
        getPresenterStatus(p->id);
    } else {
        getLiveInfo();
    }
}

void RecordTask::terminateImpl()
{
    if (request_id_ != 0) {
        recorder_->getLiveInfoHelper()->cancel(request_id_);
        request_id_ = 0;
    }
    if (stream_recorder_) {
        disconnect(stream_recorder_, nullptr, this, nullptr);
        stream_recorder_->terminate();
        stream_recorder_->deleteLater();
        stream_recorder_ = nullptr;
    }
}

void RecordTask::getPresenterStatus(int id)
{
    YingCeService::instance()->getPresenterStatus(
            id,
            yc::ResponseHandler(
                    this, [this](const yc::Request& request, const yc::Response& response) {
                        Q_UNUSED(request)
                        if (response.status() == yc::OK_CODE) {
                            const yc::Presenter pr { response.data().toObject() };
                            if (pr.status == yc::ACTIVE_LIVE_STATUS) {
                                YingCe::instance()->model()->presenterModel()->setPresenterStatus(
                                        pr.id, pr.status, pr.active_live, pr.audience_count,
                                        pr.update_time, pr.updater);
                                if (startRecord(pr.active_live)) {
                                    return;
                                }
                            }
                            getLiveInfo();
                        } else {
                            Q_EMIT failed(yc::LOGIC_ERROR, tr("Failed to get presenter info."));
                        }
                    }));
}

void RecordTask::getActiveLive(int id)
{
    YingCeService::instance()->getLive(
            id,
            yc::ResponseHandler(
                    this, [this](const yc::Request& request, const yc::Response& response) {
                        Q_UNUSED(request)
                        if (response.status() == yc::OK_CODE) {
                            const auto li =
                                    ServerLiveInfo::fromJsonObject(response.data().toObject())
                                            .toLiveInfo();
                            YingCe::instance()->model()->liveModel()->add(li);
                            const auto now = QDateTime::currentDateTime();
                            if (li.update_time.msecsTo(now) < 5000
                                && (li.stream_url.isValid() || li.web_stream_url.isValid())) {
                                startStreamRecord(li);
                            } else {
                                getLiveInfo();
                            }
                        } else {
                            getLiveInfo();
                        }
                    }));
}

bool RecordTask::startRecord(int live_id)
{
    if (live_id != 0) {
        if (auto q = YingCe::instance()->model()->liveModel()->live(live_id)) {
            const auto now = QDateTime::currentDateTime();
            if (q->update_time.msecsTo(now) < 5000
                && (q->stream_url.isValid() || q->web_stream_url.isValid())) {
                startStreamRecord(*q);
                return true;
            }
        } else {
            getActiveLive(live_id);
            return true;
        }
    }
    return false;
}

void RecordTask::getLiveInfo()
{
    auto a = YingCe::instance()->model()->attentionModel()->attention(attention_);
    Q_ASSERT(a);
    auto p = YingCe::instance()->model()->presenterModel()->presenter(a->presenter);
    Q_ASSERT(p);
    request_id_ = recorder_->getLiveInfoHelper()->get(
            p->platform, p->platform_id,
            LiveHandler(this, [this](int attention, const QString& args) {
                Q_UNUSED(attention)
                request_id_ = 0;
                if (args.isEmpty()) {
                    Q_EMIT failed(yc::LOGIC_ERROR, tr("Can't get the room info."));
                } else {
                    const auto root = QJsonDocument::fromJson(args.toUtf8()).object();
                    yc::LiveInfo live_info = yc::LiveInfo::fromJsonObject(root);
                    if (live_info.status == yc::LiveStatus::ACTIVE_LIVE_STATUS) {
                        if (auto a = YingCe::instance()->model()->attentionModel()->attention(
                                    attention_)) {
                            live_info.id = yc::IdGenerator::next();
                            live_info.presenter = a->presenter;
                            live_info.owner = a->presenter;
                            // 添加直播，添加成功后，开始录制
                            addLive(live_info);
                        } else {
                            Q_EMIT failed(yc::LOGIC_ERROR, QString());
                        }
                    } else {
                        Q_EMIT failed(yc::LOGIC_ERROR, tr("No live."));
                    }
                }
            }));
}

void RecordTask::addLive(const yc::LiveInfo& live_info)
{
    // 直接加到本地直播列表
    YingCe::instance()->model()->liveModel()->add(live_info);
    // 更新主播状态
    YingCe::instance()->model()->presenterModel()->setPresenterStatus(
            live_info.presenter, yc::ACTIVE_LIVE_STATUS, live_info.id, live_info.view_count,
            QDateTime::currentDateTime(), 0);
    // 开始录制
    startStreamRecord(live_info);
}

void RecordTask::startStreamRecord(const yc::LiveInfo& live_info)
{
    const auto& stream_url = live_info.stream_url;
    const auto& web_stream_url = live_info.web_stream_url;
    QStringList urls;
    // TODO: 暂时屏蔽full_hd1(蓝光 质臻)，后续作为会员专享
    // hls, full_hd1, hd1
    // if (!stream_url.hls_pull_url_map.full_hd1.isEmpty())
    //     urls << stream_url.hls_pull_url_map.full_hd1;
    if (!stream_url.hls_pull_url_map.hd1.isEmpty())
        urls << stream_url.hls_pull_url_map.hd1;
    // if (!web_stream_url.hls_pull_url_map.full_hd1.isEmpty())
    //     urls << web_stream_url.hls_pull_url_map.full_hd1;
    if (!web_stream_url.hls_pull_url_map.hd1.isEmpty())
        urls << web_stream_url.hls_pull_url_map.hd1;

    // hls, default
    if (!stream_url.hls_pull_url.isEmpty())
        urls << stream_url.hls_pull_url;

    // flv, full_hd1, hd1
    // if (!stream_url.flv_pull_url.full_hd1.isEmpty())
    //     urls << stream_url.flv_pull_url.full_hd1;
    if (!stream_url.flv_pull_url.hd1.isEmpty())
        urls << stream_url.flv_pull_url.hd1;
    // if (!web_stream_url.flv_pull_url.full_hd1.isEmpty())
    //     urls << web_stream_url.flv_pull_url.full_hd1;
    if (!web_stream_url.flv_pull_url.hd1.isEmpty())
        urls << web_stream_url.flv_pull_url.hd1;

    // hls, sd1, sd2
    if (!stream_url.hls_pull_url_map.sd1.isEmpty())
        urls << stream_url.hls_pull_url_map.sd1;
    if (!stream_url.hls_pull_url_map.sd2.isEmpty())
        urls << stream_url.hls_pull_url_map.sd2;
    if (!web_stream_url.hls_pull_url_map.sd1.isEmpty())
        urls << web_stream_url.hls_pull_url_map.sd1;
    if (!web_stream_url.hls_pull_url_map.sd2.isEmpty())
        urls << web_stream_url.hls_pull_url_map.sd2;

    // flv, sd1, sd2
    if (!stream_url.flv_pull_url.sd1.isEmpty())
        urls << stream_url.flv_pull_url.sd1;
    if (!stream_url.flv_pull_url.sd2.isEmpty())
        urls << stream_url.flv_pull_url.sd2;
    if (!web_stream_url.flv_pull_url.sd1.isEmpty())
        urls << web_stream_url.flv_pull_url.sd1;
    if (!web_stream_url.flv_pull_url.sd2.isEmpty())
        urls << web_stream_url.flv_pull_url.sd2;

    if (urls.empty()) {
        Q_EMIT failed(yc::LOGIC_ERROR, tr("No invalid stream url."));
        return;
    }

    start_time_ = QDateTime::currentDateTime();

    const QDir video_dir { LiveRecorder::createAttentionDirectory(attention_) };
    const auto now = QDateTime::currentDateTime();
    const QString video_name = now.toString("yyyy-MM-ddThh-mm-ss") + ".mp4";
    StreamRecorderConfig config { QString(), video_dir, video_name, QStringList() };
    stream_recorder_ = new StreamRecorderWithFailover(config, urls, this);
    connect(stream_recorder_, &StreamRecorderWithFailover::positionChanged, this,
            &RecordTask::positionChanged);
    connect(stream_recorder_, &StreamRecorderWithFailover::finished, this,
            &RecordTask::onRecordFinished);
    connect(stream_recorder_, &StreamRecorderWithFailover::errorOccurred, this,
            &RecordTask::failed);
    stream_recorder_->start();
}

void RecordTask::onRecordFinished(const QString& filename, qint64 duration)
{
    auto a = YingCe::instance()->model()->attentionModel()->attention(attention_);
    Q_ASSERT(a);
    auto p = YingCe::instance()->model()->presenterModel()->presenter(a->presenter);
    Q_ASSERT(p);
    yc::RecordInfo ri;
    ri.presenter = a->presenter;
    ri.live = p->active_live;
    ri.start_time = start_time_;
    ri.duration = duration;
    ri.url = QFileInfo(filename).fileName();
    YingCeService::instance()->addRecord(
            ri,
            yc::ResponseHandler(this,
                                [this](const yc::Request& request, const yc::Response& response) {
                                    Q_UNUSED(request)
                                    if (response.status() == yc::OK_CODE) {
                                        const yc::RecordInfo ri { response.data().toObject() };
                                        auto rm = YingCe::instance()->model()->recordModel();
                                        if (rm->hasPresenterTotalCount(ri.presenter)) {
                                            int count = rm->presenterTotalCount(ri.presenter) + 1;
                                            rm->setPresenterTotalCount(ri.presenter, count);
                                            YingCe::instance()->model()->recordModel()->add(ri);
                                        }
                                        Q_EMIT recordFinished(ri.url, ri.duration);
                                    } else {
                                        qDebug() << response.status() << response.msg();
                                    }
                                    Q_EMIT finished();
                                }));
}
